﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UMLSynthesizer.LangAnalyzer.Elements;
using UMLSynthesizer.LangAnalyzer.Statements;

namespace UMLSynthesizer.LangAnalyzer.ExtensionMethods
{
    public static class StatementExtension
    {
        /// <summary>
        /// Get all descendant statements from this
        /// </summary>
        /// <param name="root">The root statement</param>
        /// <returns>List of descendant statements</returns>
        public static IEnumerable<IStatementBase> GetDescendantStatements(this IBlockStatementBase root)
        {
            List<IStatementBase> elementList = new List<IStatementBase>();

            var childElements = root.ContainedStatements;
            elementList.AddRange(childElements);
            foreach (var element in childElements)
            {
                var containerElement = element as IBlockStatementBase;
                if (containerElement != null)
                    elementList.AddRange(GetDescendantStatements(containerElement));
            }

            return elementList;
        }

        /// <summary>
        /// Return the list of used types
        /// </summary>
        /// <param name="statement">The statement to analyze</param>
        /// <returns>the list of types</returns>
        public static IEnumerable<ITypeBase> GetUsedTypes(this IStatementBase statement)
        {
            List<ITypeBase> typeUsedList = new List<ITypeBase>();

            if (statement as IBlockStatementBase != null)
                typeUsedList.AddRange((statement as IBlockStatementBase).ContainedStatements
                    .SelectMany(stat => stat.GetUsedTypes()));
            if (statement as IBlockWithExpressionBase != null)
                typeUsedList.AddRange((statement as IBlockWithExpressionBase).ControlExpression.GetUsedTypes());
            if (statement as ICatchClause != null)
                if ((statement as ICatchClause).HasType)
                    typeUsedList.Add((statement as ICatchClause).Type);
            if (statement as IExpressionStatement != null)
                typeUsedList.AddRange((statement as IExpressionStatement).Expression.GetUsedTypes());
            if (statement as IForStatement != null)
            {
                typeUsedList.AddRange((statement as IForStatement).Declaration.GetUsedTypes());
                typeUsedList.AddRange((statement as IForStatement).IncrementorExpressions
                    .SelectMany(expression => expression.GetUsedTypes()));
            }
            if (statement as IGotoStatement != null)
                typeUsedList.AddRange((statement as IGotoStatement).Expression.GetUsedTypes());
            if (statement as IIfStatement != null)
                typeUsedList.AddRange((statement as IIfStatement).Else.GetUsedTypes());
            if (statement as ILocalDeclarationStatement != null)
            {
                typeUsedList.Add((statement as ILocalDeclarationStatement).Type);
                typeUsedList.AddRange((statement as ILocalDeclarationStatement).TypeArgumentList);
                typeUsedList.AddRange((statement as ILocalDeclarationStatement).VariableInitializerExpressions
                    .SelectMany(couple => couple.Value.GetUsedTypes()));
            }
            if (statement as IReturnStatement != null)
                typeUsedList.AddRange((statement as IReturnStatement).Expression.GetUsedTypes());
            if (statement as IThrowStatement != null)
                typeUsedList.AddRange((statement as IThrowStatement).Expression.GetUsedTypes());
            if (statement as ITryStatement != null)
            {
                typeUsedList.AddRange((statement as ITryStatement).Catches
                    .SelectMany(catchClause => catchClause.GetUsedTypes()));
                typeUsedList.AddRange((statement as ITryStatement).Finally.GetUsedTypes());
            }

            return typeUsedList.Distinct().Where(type => type != null);
        }
    }
}
